Skip to content

Add support for Evo2 predict using a LoRA#1065

Merged
balvisio merged 1 commit intomainfrom
dev/ba/BIONEMO-2617-support-predict-evo2lora
Oct 5, 2025
Merged

Add support for Evo2 predict using a LoRA#1065
balvisio merged 1 commit intomainfrom
dev/ba/BIONEMO-2617-support-predict-evo2lora

Conversation

@balvisio
Copy link
Collaborator

@balvisio balvisio commented Aug 23, 2025

Description

Adds support for Evo2 predict using a LoRA checkpoint

Type of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Refactor
  • Documentation update
  • Other (please describe):

CI Pipeline Configuration

Configure CI behavior by applying the relevant labels:

Note

By default, the notebooks validation tests are skipped unless explicitly enabled.

Authorizing CI Runs

We use copy-pr-bot to manage authorization of CI
runs on NVIDIA's compute resources.

  • If a pull request is opened by a trusted user and contains only trusted changes, the pull request's code will
    automatically be copied to a pull-request/ prefixed branch in the source repository (e.g. pull-request/123)
  • If a pull request is opened by an untrusted user or contains untrusted changes, an NVIDIA org member must leave an
    /ok to test comment on the pull request to trigger CI. This will need to be done for each new commit.

Usage

# TODO: Add code snippet

Pre-submit Checklist

  • I have tested these changes locally
  • I have updated the documentation accordingly
  • I have added/updated tests as needed
  • All existing tests pass successfully

Summary by CodeRabbit

  • New Features

    • Prediction: add CLI flag and Python API option to load a LoRA checkpoint for inference.
    • Training: add CLI options to configure LoRA hyperparameters (alpha and dimension) for fine-tuning.
  • Tests

    • Add test helper to build prediction commands and an integration test comparing predictions with and without LoRA; a duplicate test entry was also introduced.

@copy-pr-bot
Copy link

copy-pr-bot bot commented Aug 23, 2025

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@balvisio balvisio force-pushed the dev/ba/BIONEMO-2617-support-predict-evo2lora branch from c91aef2 to d98c7d4 Compare August 23, 2025 09:16
@balvisio
Copy link
Collaborator Author

/ok to test d98c7d4

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 2, 2025

Walkthrough

Adds optional LoRA support to prediction via a new CLI/API flag and model_transform passed to HyenaPredictor, introduces HyenaPredictor.configure_model, centralizes PredictionWriter into Trainer callbacks, extends training CLI with LoRA hyperparameters, adds a predict_cmd test helper and a duplicated PEFT comparison test.

Changes

Cohort / File(s) Summary of Changes
Prediction flow & LoRA integration
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py
Adds --lora-checkpoint-path CLI option; predict() gains `lora_checkpoint_path: Path
Training LoRA hyperparameters
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py
Adds --lora-alpha and --lora-dim CLI options; when lora_finetune enabled, collects non-None values into lora_kwargs and passes them to Evo2LoRA(peft_ckpt_path=..., **lora_kwargs) to construct the LoRA transform.
Test helpers
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py
Adds predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str = "") to build predict_evo2 CLI invocations aligned with training settings.
Prediction tests (PEFT vs non-PEFT)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py
Adds imports (run_command_in_subprocess, predict_cmd, small_training_finetune_cmd) and a test that runs predictions with and without PEFT and compares outputs/logits; the new test function is duplicated (appears twice).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User/CLI
  participant PA as parse_args
  participant P as predict()
  participant L as Evo2LoRA (optional)
  participant H as HyenaPredictor
  participant T as Trainer
  participant W as PredictionWriter

  U->>PA: predict_evo2 --lora-checkpoint-path=...
  PA->>P: args (incl. lora_checkpoint_path)
  P->>W: create PredictionWriter -> callbacks
  alt lora_checkpoint_path provided
    P->>L: load Evo2LoRA from checkpoint
    P->>H: instantiate HyenaPredictor(model_transform=Evo2LoRA)
  else no lora
    P->>H: instantiate HyenaPredictor(no transform)
  end
  P->>T: start Trainer with callbacks
  T->>H: run prediction
  T->>W: write predictions via callback
Loading
sequenceDiagram
  autonumber
  participant U as User/CLI
  participant PA as train parse_args
  participant TR as train()
  participant EL as Evo2LoRA

  U->>PA: train_evo2 --lora-finetune --lora-alpha X --lora-dim Y
  PA->>TR: args (alpha, dim)
  TR->>TR: lora_kwargs = {alpha?: X, dim?: Y}
  TR->>EL: Evo2LoRA(peft_ckpt_path=args.lora_checkpoint_path, **lora_kwargs)
  EL-->>TR: lora_transform applied during model build
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I hop through code with whiskers bright,
I stitch in LoRA late at night.
Callbacks hum while checkpoints gleam,
Two runs compare — a testing dream.
Carrot tucked, I patch and write. 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description follows most of the template structure but leaves the Usage section’s code example as a TODO and has not confirmed that “All existing tests pass” in the Pre-submit Checklist, indicating incomplete required information. Please provide a concrete code snippet in the Usage section demonstrating the new LoRA predict feature and update the Pre-submit Checklist to confirm that all existing tests pass successfully.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly describes the main feature added, namely enabling Evo2’s predict functionality to accept a LoRA checkpoint, and is concise and focused on the core change.
Docstring Coverage ✅ Passed Docstring coverage is 81.82% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev/ba/BIONEMO-2617-support-predict-evo2lora

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 81680c9 and 5896940.

📒 Files selected for processing (4)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (7 hunks)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (2 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (1 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (3)
sub-packages/bionemo-testing/src/bionemo/testing/subprocess_utils.py (1)
  • run_command_in_subprocess (108-129)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
  • predict_cmd (63-71)
  • small_training_finetune_cmd (40-60)
sub-packages/bionemo-testing/src/bionemo/testing/data/fasta.py (1)
  • create_fasta_file (28-63)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (1)
sub-packages/bionemo-evo2/src/bionemo/evo2/models/peft.py (1)
  • Evo2LoRA (26-279)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (3)
sub-packages/bionemo-evo2/src/bionemo/evo2/models/peft.py (1)
  • Evo2LoRA (26-279)
sub-packages/bionemo-llm/src/bionemo/llm/lightning.py (1)
  • configure_model (307-329)
sub-packages/bionemo-llm/src/bionemo/llm/utils/callbacks.py (1)
  • PredictionWriter (38-225)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: changed-files
  • GitHub Check: pre-commit
  • GitHub Check: Analyze (rust)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jwilber
Copy link
Collaborator

jwilber commented Sep 2, 2025

/ok to test 12a3919

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (3)

155-157: Fix CP all-gather: wrong process group used (will hang or produce wrong logits).

_gather_along_cp_dim() computes CP world size but gathers on the TP group.

Apply:

-    torch.distributed.all_gather_into_tensor(
-        output, input_.contiguous(), group=parallel_state.get_tensor_model_parallel_group()
-    )
+    torch.distributed.all_gather_into_tensor(
+        output, input_.contiguous(), group=parallel_state.get_context_parallel_group()
+    )

352-356: Handle output_dir=None from CLI (current code will crash).

parse_args defaults output-dir to None but predict() unconditionally calls mkdir on it.

Apply:

     if work_dir is None:
         work_dir = Path(tempfile.mkdtemp())
     sequence_parallel = tensor_parallel_size > 1 and not no_sequence_parallel
-    output_dir.mkdir(parents=True, exist_ok=True)  # Make sure the output directory exists, files will be written here.
+    if output_dir is None:
+        output_dir = work_dir / "predictions"
+    output_dir.mkdir(parents=True, exist_ok=True)

Alternatively, make --output-dir required in parse_args.


595-633: MambaPredictor predict_step: align outputs with Hyena and return seq_idx; current log-prob logic is offset and non-vectorized.

  • Compute next-token log-probs like Hyena (drop first logit, gather by input_ids[:,1:], mask).
  • Include seq_idx for downstream reconstruction.
  • Avoid accumulating self.predictions across steps in the returned payload to prevent duplication.
-    def predict_step(self, batch, batch_idx: int, dataloader_idx: int = 0) -> dict:
-        """Prediction step, saving the results."""
-        # Get the tokens and attention mask
-        if batch == {}:
-            batch = {"tokens": [], "position_ids": [], "attention_mask": []}
-        inference_data = {"tokens": batch["tokens"], "position_ids": batch["position_ids"]}
-
-        if self.trainer.strategy._cp_size > 1:
-            inference_data = get_batch_on_this_context_parallel_rank(
-                inference_data,
-                num_micro_batches=1,
-                micro_batch_idx=0,
-                sequence_parallel=False,
-                micro_batch_size_per_context_rank=None,
-                context_parallel_size=self.trainer.strategy._cp_size,
-                sequence_length=self.config.seq_length,
-                multiple_of=1,
-            )
-            inference_data["attention_mask"] = None
-
-        with nullcontext() if torch.is_inference_mode_enabled() else torch.no_grad():
-            output = self(
-                input_ids=inference_data["tokens"],
-                position_ids=inference_data["position_ids"],
-                inference_params=None,
-                attention_mask=inference_data.get("attention_mask", None),
-            )
-
-            if self.trainer.strategy._tp_size > 1:
-                # TP > 1 case - all-gather the output tensor along last dimension from all TP ranks for each token
-                # [b, s, v/p] -> [b, s, v]
-                output = _gather_along_last_dim(output)
-
-            # Cache predictions
-            self.predictions.extend(torch.argmax(output, dim=-1).cpu().numpy().tolist())
-
-        # Return as dict for PredictionWriter callback
-        return {"predictions": self.predictions, "log_probabilities": self.log_probabilities, "tokens": self.tokens}
+    def predict_step(self, batch, batch_idx: int, dataloader_idx: int = 0) -> dict:
+        """Prediction step aligning with Hyena outputs; returns either token logits or sequence log-probs."""
+        if not batch:
+            return {}
+        inference_data = {
+            "tokens": batch["tokens"],
+            "position_ids": batch["position_ids"],
+            "loss_mask": batch.get("loss_mask"),
+            "seq_idx": batch.get("seq_idx"),
+        }
+        if self.trainer.strategy._cp_size > 1:
+            inference_data = get_batch_on_this_context_parallel_rank(
+                inference_data,
+                num_micro_batches=1,
+                micro_batch_idx=0,
+                sequence_parallel=False,
+                micro_batch_size_per_context_rank=None,
+                context_parallel_size=self.trainer.strategy._cp_size,
+                sequence_length=self.config.seq_length,
+                multiple_of=1,
+            )
+            inference_data["attention_mask"] = None
+
+        with nullcontext() if torch.is_inference_mode_enabled() else torch.no_grad():
+            output = self(
+                input_ids=inference_data["tokens"],
+                position_ids=inference_data["position_ids"],
+                inference_params=None,
+                attention_mask=inference_data.get("attention_mask", None),
+            )
+            if self.trainer.strategy._tp_size > 1:
+                output = _gather_along_last_dim(output)
+
+        if self.output_log_prob_seqs:
+            # Align predictions with next-token targets
+            softmax_logprobs = torch.log_softmax(output, dim=-1)[:, :-1]
+            input_ids = inference_data["tokens"][:, 1:]
+            logprobs = torch.gather(softmax_logprobs, 2, input_ids.unsqueeze(-1)).squeeze(-1)
+            if inference_data.get("loss_mask") is not None:
+                logprobs = logprobs * inference_data["loss_mask"][:, 1:].float()
+            if self.log_prob_collapse_option == "per_token":
+                return {"log_probs_seqs": logprobs.cpu(), "seq_idx": inference_data["seq_idx"].cpu()}
+            else:
+                seq_log = torch.sum(logprobs, dim=1)
+                if self.log_prob_collapse_option == "mean":
+                    denom = (inference_data["loss_mask"][:, 1:].float().sum(dim=-1) + 1e-8)
+                    seq_log = seq_log / denom
+                return {"log_probs_seqs": seq_log.cpu(), "seq_idx": inference_data["seq_idx"].cpu()}
+        else:
+            return {
+                "token_logits": output.cpu(),
+                "pad_mask": inference_data.get("loss_mask", None).cpu() if inference_data.get("loss_mask") is not None else None,
+                "seq_idx": inference_data.get("seq_idx", None).cpu() if inference_data.get("seq_idx") is not None else None,
+            }
🧹 Nitpick comments (12)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (4)

23-27: Deduplicate --limit-val-batches flag (appears twice).
Harmless but noisy; keep a single occurrence in each command builder.

Apply this diff in both builders:

-        "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* --limit-val-batches 1 "
+        "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* "
-        f"--max-steps {max_steps} --warmup-steps 1 --val-check-interval {val_check} --limit-val-batches 1 "
+        f"--max-steps {max_steps} --warmup-steps 1 --val-check-interval {val_check} --limit-val-batches 1 "

Also applies to: 43-49


23-28: Quote wildcard argument and path-like values to avoid shell globbing and whitespace issues.
SDH* can expand in a shell; paths with spaces can break. Quote the pattern and use shlex.quote for dynamic paths.

Apply this diff:

+import shlex
@@
-        f"train_evo2 --mock-data --result-dir {path} --devices {devices} "
-        "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* --limit-val-batches 1 "
+        f"train_evo2 --mock-data --result-dir {shlex.quote(str(path))} --devices {devices} "
+        "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern 'SDH*' --limit-val-batches 1 "
@@
-        f"train_evo2 --mock-data --result-dir {path} --devices {devices} "
-        "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* --limit-val-batches 1 "
+        f"train_evo2 --mock-data --result-dir {shlex.quote(str(path))} --devices {devices} "
+        "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern 'SDH*' --limit-val-batches 1 "
@@
-        f"predict_evo2 --fasta {fasta_file_path} --ckpt-dir {ckpt_dir} --output-dir {output_dir} "
-        "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* --tensor-parallel-size 1 "
+        f"predict_evo2 --fasta {shlex.quote(str(fasta_file_path))} --ckpt-dir {shlex.quote(str(ckpt_dir))} --output-dir {shlex.quote(str(output_dir))} "
+        "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern 'SDH*' --tensor-parallel-size 1 "

Additional change outside hunks (add import at top of file):

+import shlex

Also applies to: 43-49, 56-59


53-55: Fix docstring typo.
“fro” → “for”.

Apply:

-    """Command fro predict."""
+    """Command for predict."""

53-53: PEP8 spacing in signature.
Add a space around '='.

-def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str=""):
+def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str = ""):
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_train.py (1)

40-50: Reduce noise: print captured streams only on failure.
Always printing stdout/stderr makes CI logs noisy.

-    print("Captured STDOUT:\n", train_stdout)
-    print("Captured STDERR:\n", train_stderr)
+    # Consider logging on failure only, or behind a verbosity flag.
+    # print("Captured STDOUT:\n", train_stdout)
+    # print("Captured STDERR:\n", train_stderr)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_lora.py (2)

205-210: Load prediction tensors onto CPU to avoid device mismatches.
Prevents accidental CUDA dependency during test artifact inspection.

-    results_original = torch.load(f"{result_dir_original}/predictions__rank_0.pt")
-    results_peft = torch.load(f"{result_dir_peft}/predictions__rank_0.pt")
+    results_original = torch.load(f"{result_dir_original}/predictions__rank_0.pt", map_location="cpu")
+    results_peft = torch.load(f"{result_dir_peft}/predictions__rank_0.pt", map_location="cpu")

126-129: Minor: variable naming for resumed finetune output.
Rename to avoid shadowing and clarify intent.

-        stdout_finetune: str = run_command_in_subprocess(command=command_resume_finetune, path=str(tmp_path))
+        stdout_finetune_resume: str = run_command_in_subprocess(command=command_resume_finetune, path=str(tmp_path))
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (5)

339-340: Include 'per_token' in type hint.

CLI allows per_token and Hyena path handles it; signature excludes it.

-    log_prob_collapse_option: Literal["sum", "mean"] = "mean",
+    log_prob_collapse_option: Literal["sum", "mean", "per_token"] = "mean",

380-381: Confirm tokenizer API.

get_nmt_tokenizer("byte-level") assumes positional arg = library; Nemo versions sometimes require keyword library=....

If needed:

-    tokenizer = get_nmt_tokenizer("byte-level")
+    tokenizer = get_nmt_tokenizer(library="byte-level")

Also consider pulling library from model config for parity with train-time tokenizer.


432-468: Minor trainer config nits.

  • limit_val_batches has no effect for predict; consider limit_predict_batches if needed.
  • sequence_parallel argument repeats the TP>1 check already encoded in sequence_parallel var.
-            sequence_parallel=tensor_parallel_size > 1 and sequence_parallel,
+            sequence_parallel=sequence_parallel,
-        log_every_n_steps=1,
-        limit_val_batches=10,
+        log_every_n_steps=1,
+        # limit_predict_batches=...  # set if needed for smoke tests

134-140: CLI wiring for LoRA looks good; consider making the behavior explicit in help.

E.g., note that LoRA is currently applied only for model_type="hyena".

If you adopt the warning for Mamba, no change needed.


448-454: Sampler seq_len hard-coded to 8192.

Ensure this matches the active config (e.g., 1m models). Otherwise, expose as arg or derive from config.seq_length.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a29272f and 12a3919.

📒 Files selected for processing (7)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/peft.py (0 hunks)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (8 hunks)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (2 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/__init__.py (1 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (1 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_lora.py (1 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_train.py (1 hunks)
💤 Files with no reviewable changes (1)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/peft.py
🧰 Additional context used
🧬 Code graph analysis (3)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_lora.py (3)
sub-packages/bionemo-testing/src/bionemo/testing/data/fasta.py (1)
  • create_fasta_file (28-63)
sub-packages/bionemo-testing/src/bionemo/testing/subprocess_utils.py (1)
  • run_command_in_subprocess (108-129)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (3)
  • predict_cmd (53-61)
  • small_training_cmd (20-29)
  • small_training_finetune_cmd (32-50)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_train.py (1)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
  • small_training_cmd (20-29)
  • small_training_finetune_cmd (32-50)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (5)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/peft.py (1)
  • Evo2LoRA (26-279)
sub-packages/bionemo-evo2/src/bionemo/evo2/models/mamba.py (1)
  • configure_model (356-383)
sub-packages/bionemo-llm/src/bionemo/llm/lightning.py (1)
  • configure_model (307-329)
sub-packages/bionemo-llm/src/bionemo/llm/utils/callbacks.py (1)
  • PredictionWriter (31-154)
sub-packages/bionemo-evo2/src/bionemo/evo2/utils/checkpoint/convert_to_nemo.py (2)
  • tokenizer (137-149)
  • config (152-158)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (7)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/__init__.py (1)

1-14: LGTM — package marker only.
Header-only file to enable test discovery; no action needed.

sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (2)

495-496: CLI type for --lora-checkpoint-path changed to str — confirm downstream expectations.
If any consumer expects a PathLike, use os.fspath() or Path() at the usage site to avoid surprises.

Apply (at the creation site) if needed:

-            lora_transform = Evo2LoRA(peft_ckpt_path=args.lora_checkpoint_path)
+            from os import fspath
+            lora_transform = Evo2LoRA(peft_ckpt_path=fspath(args.lora_checkpoint_path) if args.lora_checkpoint_path else None)

629-634: Avoid double-wiring Evo2LoRA (model_transform and callback) unless required.
Passing lora_transform to both HyenaModel(..., model_transform=...) and as a Lightning callback might duplicate effects.

Please confirm Evo2LoRA’s intended integration point. If it’s a callback, drop the model_transform= argument; if it’s a config/model transform, don’t also add it as a callback. I can propose a follow-up diff once confirmed.

Also applies to: 658-659

sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_train.py (2)

33-34: Good reuse of shared helpers.
Importing from common reduces duplication across tests.


53-61: Confirm intentional seq-length change between Mamba pretrain (8) and finetune (16).
If unintentional, align the values; if intentional, add a brief comment explaining the rationale.

Also applies to: 64-74

sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_lora.py (1)

30-114: Nice coverage — LoRA on/off matrix with resume path.
End-to-end checks for logs, checkpoints, and TB outputs look solid.

sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (1)

41-43: Import Evo2LoRA.

Import looks correct and localized to Hyena path only.

@dorotat-nv
Copy link
Collaborator

@balvisio , evo2 finetune + lora does not work, see #1136

Copy link
Collaborator

@dorotat-nv dorotat-nv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comments to #1066

@balvisio balvisio force-pushed the dev/ba/BIONEMO-2617-support-predict-evo2lora branch from 12a3919 to 35dc042 Compare September 23, 2025 15:46
@balvisio
Copy link
Collaborator Author

/ok to test 35dc042

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (1)

634-634: Type inconsistency for --lora-checkpoint-path argument.

The type is changed from Path to str in train.py, but it remains Path in predict.py (line 165). This inconsistency could cause confusion.

Apply this diff to maintain consistency:

-    parser.add_argument("--lora-checkpoint-path", type=str, default=None, help="LoRA checkpoint path")
+    parser.add_argument("--lora-checkpoint-path", type=Path, default=None, help="LoRA checkpoint path")

Then update line 821 to use the Path object:

-            lora_transform = Evo2LoRA(peft_ckpt_path=args.lora_checkpoint_path)
+            lora_transform = Evo2LoRA(peft_ckpt_path=str(args.lora_checkpoint_path) if args.lora_checkpoint_path else None)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (1)

441-450: Consider using batch writer for multi-device compatibility.

The PredictionWriter is set to write_interval="epoch" which may cause issues when num_devices > 1 (common in multi-parallel scenarios). The batch writer is more compatible with distributed setups.

Consider using batch writer as the default or make it configurable based on the parallelism settings:

 callbacks = [
     PredictionWriter(
         output_dir=output_dir,
-        write_interval=write_interval,
+        write_interval="batch" if (tensor_parallel_size * pipeline_model_parallel_size * context_parallel_size) > 1 else write_interval,
         batch_dim_key_defaults={"token_logits": 0},
         seq_dim_key_defaults={"token_logits": 1},
         files_per_subdir=files_per_subdir,
         save_all_model_parallel_ranks=False,  # only write one copy of predictions.
     )
 ]
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (3)

30-36: Quote glob pattern and paths to avoid shell globbing and path breakage.

When executed via a shell, SDH* may expand to filesystem matches; unquoted paths may break on spaces. Quote SDH* and path-like args.

Apply:

-        f"train_evo2 --mock-data --result-dir {path} --devices {devices} "
-        "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* --limit-val-batches 1 "
+        f'train_evo2 --mock-data --result-dir "{path}" --devices {devices} '
+        "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern 'SDH*' --limit-val-batches 1 "
@@
-        f"train_evo2 --mock-data --result-dir {path} --devices {devices} "
-        "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* --limit-val-batches 1 "
+        f'train_evo2 --mock-data --result-dir "{path}" --devices {devices} '
+        "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern 'SDH*' --limit-val-batches 1 "
@@
-        f"--seq-length 16 --hidden-dropout 0.1 --attention-dropout 0.1 {additional_args} --ckpt-dir {prev_ckpt} "
+        f'--seq-length 16 --hidden-dropout 0.1 --attention-dropout 0.1 {additional_args} --ckpt-dir "{prev_ckpt}" '
@@
-        f"predict_evo2 --fasta {fasta_file_path} --ckpt-dir {ckpt_dir} --output-dir {output_dir} "
-        "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* --tensor-parallel-size 1 "
+        f'predict_evo2 --fasta "{fasta_file_path}" --ckpt-dir "{ckpt_dir}" --output-dir "{output_dir}" '
+        "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern 'SDH*' --tensor-parallel-size 1 "

Longer-term, prefer constructing argument lists and passing to subprocess with shell=False to avoid quoting concerns.

Also applies to: 52-59, 66-69


64-64: Fix typo in docstring (“fro” → “for”).

Apply:

-    """Command fro predict."""
+    """Command for predict."""

63-71: Expose LoRA checkpoint as a first-class arg in predict_cmd (use --lora-checkpoint-path).

Add parameter lora_checkpoint_path: str | None = None to predict_cmd and, if set, append --lora-checkpoint-path {lora_checkpoint_path} to the constructed command; keep additional_args for backward-compatible flexibility. Confirmed CLI flag --lora-checkpoint-path in sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 12a3919 and 35dc042.

📒 Files selected for processing (7)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/peft.py (0 hunks)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (7 hunks)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (4 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/__init__.py (1 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (1 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_finetune.py (1 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_train.py (1 hunks)
💤 Files with no reviewable changes (1)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/peft.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/init.py
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_train.py
🧰 Additional context used
🧬 Code graph analysis (2)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_finetune.py (4)
sub-packages/bionemo-testing/src/bionemo/testing/data/fasta.py (1)
  • create_fasta_file (28-63)
sub-packages/bionemo-testing/src/bionemo/testing/subprocess_utils.py (1)
  • run_command_in_subprocess (108-129)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (3)
  • predict_cmd (63-71)
  • small_training_cmd (20-37)
  • small_training_finetune_cmd (40-60)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_train.py (1)
  • test_train_evo2_finetune_runs (103-171)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (3)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/peft.py (1)
  • Evo2LoRA (26-279)
sub-packages/bionemo-evo2/src/bionemo/evo2/models/mamba.py (1)
  • configure_model (356-383)
sub-packages/bionemo-llm/src/bionemo/llm/lightning.py (1)
  • configure_model (307-329)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (14)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (3)

91-110: LGTM! Mock data flags provide useful configurability.

The addition of mock dataset size control parameters allows flexible testing scenarios. Default values are reasonable for most use cases.


692-694: LGTM! Mock data module properly uses new parameters.

The mock data module is correctly instantiated with the new size parameters.


848-849: Potential NameError when --lora-finetune is used with Mamba.

lora_transform is only defined in the Hyena branch (line 821), but it's appended to callbacks unconditionally at line 849, which will cause a NameError for Mamba models.

Apply this diff to fix the issue:

+    lora_transform = None
     # Create model based on selected model type
     if model_type == "hyena":
         if args.model_size not in HYENA_MODEL_OPTIONS:
             raise ValueError(f"Invalid model size for Hyena: {args.model_size}")
         model_config = HYENA_MODEL_OPTIONS[args.model_size](**config_modifiers_init)
         if args.no_weight_decay_embeddings:
             # Override the default weight decay condition for Hyena with our bionemo version that also excludes
             #  embeddings
             model_config.hyena_no_weight_decay_cond_fn = hyena_no_weight_decay_cond_with_embeddings
         # Lora adaptors configuration
-        lora_transform = None
         if args.lora_finetune:
             lora_transform = Evo2LoRA(peft_ckpt_path=args.lora_checkpoint_path)

         model = llm.HyenaModel(model_config, tokenizer=data_module.tokenizer, model_transform=lora_transform)
     elif model_type == "mamba":  # mamba

And update the callback append logic:

-    if args.lora_finetune:
-        callbacks.append(lora_transform)
+    if lora_transform is not None:
+        callbacks.append(lora_transform)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_finetune.py (4)

32-52: LGTM! Clean validation loss extraction utility.

The function provides a clean way to extract and sample validation losses from logs for monitoring training progress.


106-110: LGTM! Good validation of monotonic loss decrease.

This test ensures that the model is learning properly by checking that validation losses decrease monotonically at sampled points.


285-288: LGTM! Comprehensive LoRA finetune verification.

Good test coverage confirming that LoRA finetuning produces different outputs from the base model while maintaining consistent sequence indices and shapes.


272-272: Bug: assertion checks the wrong variable for restore message validation.

The assertion checks stdout_finetune instead of stdout_predict, so the prediction restore path isn't actually tested.

Apply this diff:

-    assert "Restoring model weights from RestoreConfig(path='" in stdout_finetune
+    assert "Restoring model weights from RestoreConfig(path='" in stdout_predict
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (5)

163-169: LGTM! LoRA support properly added to CLI.

The command-line argument for LoRA checkpoint path is properly integrated.


482-494: LGTM! LoRA properly integrated for Hyena models.

The LoRA transformation is correctly instantiated and passed to the HyenaPredictor when a LoRA checkpoint path is provided.


505-510: Warn when LoRA path is provided for Mamba models.

If a user provides a LoRA checkpoint path with a Mamba model, it will be silently ignored, which could lead to confusion.

Add a warning when LoRA is requested but not supported:

 else:  # mamba
     if model_size not in MAMBA_MODEL_OPTIONS:
         raise ValueError(f"Invalid model size for Mamba: {model_size}")
+    if lora_checkpoint_path:
+        logger.warning("LoRA checkpoint provided but LoRA is not supported for Mamba models; ignoring.")
     config = MAMBA_MODEL_OPTIONS[model_size](

278-280: LGTM! MambaPredictor class properly structured.

The MambaPredictor class correctly inherits from both BasePredictor and MambaModel for prediction functionality.


272-275: Guard trainer access in configure_model to prevent AttributeError.

If configure_model is called before the Trainer is attached, accessing self.trainer.strategy will raise an AttributeError.

Apply this diff to safely handle trainer access:

 def configure_model(self, *args, **kwargs) -> None:
     """Configure the model."""
     super().configure_model(*args, **kwargs)
-    self.trainer.strategy._init_model_parallel = True
+    if hasattr(self, "trainer") and self.trainer is not None:
+        if hasattr(self.trainer, "strategy"):
+            self.trainer.strategy._init_model_parallel = True
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)

24-26: No change required — project targets Python ≥3.10

pyproject.toml declares requires-python = ">=3.10" and CI workflows run on Python 3.11/3.13, so int | None is supported.


4-5: SPDX header: add missing period; keep LicenseRef-Apache2

File: sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (lines 4–5)

  • Add a trailing period to line 4.
  • Do not change line 5 — the repository uses LicenseRef-Apache2.

Suggested fix:

-# SPDX-FileCopyrightText: Copyright (c) 2024 Stanford University. All rights reserved
+# SPDX-FileCopyrightText: Copyright (c) 2024 Stanford University. All rights reserved.
 # SPDX-License-Identifier: LicenseRef-Apache2

Likely an incorrect or invalid review comment.

@balvisio balvisio force-pushed the dev/ba/BIONEMO-2617-support-predict-evo2lora branch 2 times, most recently from 3f40581 to f3ac5b0 Compare September 24, 2025 14:03
@balvisio balvisio changed the base branch from main to dev/ba/BIONEMO-2473-add-evo2-lora-tests September 24, 2025 14:05
@balvisio balvisio force-pushed the dev/ba/BIONEMO-2617-support-predict-evo2lora branch from f3ac5b0 to 41f9092 Compare September 24, 2025 14:06
@balvisio balvisio force-pushed the dev/ba/BIONEMO-2617-support-predict-evo2lora branch 2 times, most recently from 08e2453 to 5c25dea Compare September 25, 2025 22:43
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (1)

63-71: Fix docstring typo and keep style consistent.

Minor typo and spacing nit in signature.

-def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str = ""):
-    """Command fro predict."""
+def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str = ""):
+    """Command for predict."""
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (1)

441-451: PredictionWriter mappings incomplete for log_probs_seqs; prefer explicit dims.

When output_log_prob_seqs=True, writer needs dims for log_probs_seqs. Also future keys like predictions benefit from defaults.

 callbacks = [
     PredictionWriter(
         output_dir=output_dir,
         write_interval=write_interval,
-        batch_dim_key_defaults={"token_logits": 0},
-        seq_dim_key_defaults={"token_logits": 1},
+        batch_dim_key_defaults={"token_logits": 0, "log_probs_seqs": 0, "predictions": 0},
+        seq_dim_key_defaults={"token_logits": 1, "log_probs_seqs": 1},
         files_per_subdir=files_per_subdir,
         save_all_model_parallel_ranks=False,  # only write one copy of predictions.
     )
 ]
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0aa6afa and 08e2453.

📒 Files selected for processing (4)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (7 hunks)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (2 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (1 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py
🧰 Additional context used
🧬 Code graph analysis (2)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (3)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
  • predict_cmd (63-71)
  • small_training_finetune_cmd (40-60)
sub-packages/bionemo-testing/src/bionemo/testing/subprocess_utils.py (1)
  • run_command_in_subprocess (108-129)
sub-packages/bionemo-testing/src/bionemo/testing/data/fasta.py (1)
  • create_fasta_file (28-63)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (2)
sub-packages/bionemo-evo2/src/bionemo/evo2/models/peft.py (1)
  • Evo2LoRA (26-279)
sub-packages/bionemo-llm/src/bionemo/llm/lightning.py (1)
  • configure_model (307-329)
🪛 GitHub Actions: BioNeMo Framework CI
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py

[error] 390-390: F821: Undefined name 'small_training_finetune_cmd'


[error] 398-398: F821: Undefined name 'run_command_in_subprocess'


[error] 412-412: F821: Undefined name 'run_command_in_subprocess'


[error] 389-392: F821: Undefined name 'small_training_finetune_cmd'


[error] 396-396: F821: Undefined name 'run_command_in_subprocess'

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: verify-tests-status
  • GitHub Check: Analyze (rust)
🔇 Additional comments (10)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (6)

44-44: LoRA import addition looks correct.


163-169: CLI: LoRA path flag LGTM.

Flag name, type, and help are consistent with usage below.


482-487: LoRA integration path (Hyena) looks correct.

Transform injected via callback and model_transform; aligns with Evo2LoRA behavior.

Please confirm Evo2LoRA implements the Callback API points used during predict (setup/on_predict_epoch_start) so the transform applies before weights are loaded.


495-511: Warn or error if LoRA path provided for Mamba (currently ignored).

Avoid silent ignore; emit a clear warning (or raise).

     else:  # mamba
         if model_size not in MAMBA_MODEL_OPTIONS:
             raise ValueError(f"Invalid model size for Mamba: {model_size}")
+        if lora_checkpoint_path:
+            logger.warning(
+                f"LoRA checkpoint provided ({lora_checkpoint_path}) but LoRA is not applied for Mamba models; ignoring."
+            )
         config = MAMBA_MODEL_OPTIONS[model_size](
             forward_step_fn=hyena_predict_forward_step,  # Can reuse the same forward steps
             data_step_fn=hyena_predict_data_step,
             distribute_saved_activations=False if sequence_parallel and tensor_parallel_size > 1 else True,
             **config_modifiers_init,
         )

599-600: main forwards LoRA path correctly.


272-276: Guard trainer access in configure_model to avoid AttributeError.

self.trainer may be None here.

     def configure_model(self, *args, **kwargs) -> None:
         """Configure the model."""
         super().configure_model(*args, **kwargs)
-        self.trainer.strategy._init_model_parallel = True
+        trainer = getattr(self, "trainer", None)
+        if trainer is not None and getattr(trainer, "strategy", None) is not None:
+            trainer.strategy._init_model_parallel = True
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)

63-71: Avoid shadowing the top-level package by relocating test helper module.

Placing test helpers under tests/bionemo/... can shadow the real bionemo package during test runs. Move this file to a tests-only namespace (e.g., tests/helpers/evo2/common.py) and update imports.


63-71: Confirm ‘1b_nv’ is supported in HYENA_MODEL_OPTIONS
Ensure the predict helper’s default --model-size 1b_nv is included in the imported Nemo HYENA_MODEL_OPTIONS; manual upstream verification required.

sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (2)

433-434: Bug: assert checks wrong stdout variable after PEFT predict.

Should assert against stdout_predict.

-    assert "Restoring model weights from RestoreConfig(path='" in stdout_finetune
+    assert "Restoring model weights from RestoreConfig(path='" in stdout_predict

36-37: Pipeline blocker: missing imports (F821).

Tests reference small_training_finetune_cmd and run_command_in_subprocess but don’t import them.

-from .common import predict_cmd
+from .common import predict_cmd, small_training_finetune_cmd
+from bionemo.testing.subprocess_utils import run_command_in_subprocess

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (1)

848-850: Guard callback append when LoRA transform is unavailable

lora_transform is only defined inside the Hyena branch. For other model types (e.g., mamba) a --lora-finetune run will reach this block and crash with NameError. Initialize the variable before the branch and append only when it was actually created.

-    if args.lora_finetune:
-        callbacks.append(lora_transform)
+    if lora_transform is not None:
+        callbacks.append(lora_transform)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (1)

32-38: Import the helpers you just used

small_training_finetune_cmd and run_command_in_subprocess are referenced later but never imported, leading to the CI F821 failures. Pull them in alongside predict_cmd.

-from .common import predict_cmd
+from .common import predict_cmd, small_training_finetune_cmd
+from bionemo.testing.subprocess_utils import run_command_in_subprocess
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 08e2453 and 5c25dea.

📒 Files selected for processing (4)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (7 hunks)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (2 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (1 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (1)
sub-packages/bionemo-evo2/src/bionemo/evo2/models/peft.py (1)
  • Evo2LoRA (26-279)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (3)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
  • predict_cmd (63-71)
  • small_training_finetune_cmd (40-60)
sub-packages/bionemo-testing/src/bionemo/testing/subprocess_utils.py (1)
  • run_command_in_subprocess (108-129)
sub-packages/bionemo-testing/src/bionemo/testing/data/fasta.py (1)
  • create_fasta_file (28-63)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (3)
sub-packages/bionemo-evo2/src/bionemo/evo2/models/peft.py (1)
  • Evo2LoRA (26-279)
sub-packages/bionemo-llm/src/bionemo/llm/lightning.py (1)
  • configure_model (307-329)
sub-packages/bionemo-llm/src/bionemo/llm/utils/callbacks.py (1)
  • PredictionWriter (38-225)
🪛 GitHub Actions: BioNeMo Framework CI
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py

[error] 389-392: F821 Undefined name 'small_training_finetune_cmd'.


[error] 396-399: F821 Undefined name 'run_command_in_subprocess'.


[error] 410-413: F821 Undefined name 'run_command_in_subprocess'.


[error] 432-433: F821 Undefined name 'run_command_in_subprocess'.

🔇 Additional comments (5)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (2)

272-275: Guard trainer access in configure_model

configure_model can be invoked before the Trainer is attached, so self.trainer may be None. The unconditional attribute access will raise, re-introducing the issue flagged earlier. Guard the access as previously suggested.

     def configure_model(self, *args, **kwargs) -> None:
         """Configure the model."""
         super().configure_model(*args, **kwargs)
-        self.trainer.strategy._init_model_parallel = True
+        trainer = getattr(self, "trainer", None)
+        if trainer is not None and getattr(trainer, "strategy", None) is not None:
+            trainer.strategy._init_model_parallel = True

505-510: Fail fast when LoRA is requested for Mamba

Supplying --lora-checkpoint-path with model_type="mamba" is silently ignored, so predictions run without the expected adapters. Raise (or at least warn) so users know the configuration is unsupported instead of getting wrong results.

     else:  # mamba
         if model_size not in MAMBA_MODEL_OPTIONS:
             raise ValueError(f"Invalid model size for Mamba: {model_size}")
+        if lora_checkpoint_path:
+            raise ValueError("LoRA checkpoints are not supported for Mamba models.")
         config = MAMBA_MODEL_OPTIONS[model_size](
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (1)

433-434: Check the right stdout in the PEFT assert

This assert is still inspecting stdout_finetune, so the PEFT run output never gets validated and the test will miss regressions. Point it at stdout_predict.

-    assert "Restoring model weights from RestoreConfig(path='" in stdout_finetune
+    assert "Restoring model weights from RestoreConfig(path='" in stdout_predict
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)

63-71: Relocate test helper out of the bionemo namespace

Adding more helpers under tests/bionemo/... keeps creating a shadow package that can mask the real bionemo modules during test runs. This has already caused brittle imports when sys.path puts the tests directory ahead of src/, and extending it here increases the odds of hitting that collision as soon as more predict tests land. Please move this helper to a tests-only namespace (e.g., tests/helpers/evo2/common.py) and update the imports accordingly so we no longer install a faux top-level bionemo package in the test tree.


64-64: Fix docstring typo

The docstring still reads “Command fro predict.” Please update it to “Command for predict.”

@balvisio balvisio force-pushed the dev/ba/BIONEMO-2617-support-predict-evo2lora branch from 5c25dea to 2829015 Compare September 25, 2025 23:03
@balvisio
Copy link
Collaborator Author

/ok to test 2829015

@balvisio balvisio force-pushed the dev/ba/BIONEMO-2617-support-predict-evo2lora branch 2 times, most recently from 5db2c32 to c7d8b34 Compare September 26, 2025 00:51
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (1)

63-71: Optionally quote paths/args to be robust to spaces.

Use shlex.quote on paths and the extra args when building the string.

-def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str = ""):
+def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str = ""):
     """Command for predict."""
-    cmd = (
-        f"predict_evo2 --fasta {fasta_file_path} --ckpt-dir {ckpt_dir} --output-dir {output_dir} "
+    cmd = (
+        f"predict_evo2 --fasta {shlex.quote(str(fasta_file_path))} "
+        f"--ckpt-dir {shlex.quote(str(ckpt_dir))} --output-dir {shlex.quote(str(output_dir))} "
         "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* --tensor-parallel-size 1 "
-        f"--pipeline-model-parallel-size 1 --context-parallel-size 1 {additional_args}"
+        f"--pipeline-model-parallel-size 1 --context-parallel-size 1 {additional_args}"
     )

Add at top of file (outside diff range):

import shlex
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (1)

372-453: Make “different logits” assertion numerically robust.

Use allclose with a small tolerance instead of elementwise inequality to avoid false positives/negatives.

-    logits_original = results_original["token_logits"]
-    logits_peft = results_peft["token_logits"]
-    assert (logits_original != logits_peft).any()
+    logits_original = results_original["token_logits"]
+    logits_peft = results_peft["token_logits"]
+    # Expect a meaningful delta; fail if tensors are (near-)identical
+    assert not torch.allclose(logits_original, logits_peft, rtol=1e-5, atol=1e-6)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (2)

441-450: PredictionWriter config is incomplete for log-prob outputs; handle multi-device epoch writes.

  • Include dims for log_probs_seqs to ensure correct collation.
  • Optionally force write_interval="batch" when devices>1 to avoid epoch write limitations.
-    callbacks = [
+    # Normalize writer interval in multi-device runs
+    if (devices or 1) > 1 and write_interval == "epoch":
+        logger.warning("write_interval='epoch' with multi-device is unsupported; falling back to 'batch'.")
+        write_interval = "batch"
+
+    callbacks = [
         PredictionWriter(
             output_dir=output_dir,
             write_interval=write_interval,
-            batch_dim_key_defaults={"token_logits": 0},
-            seq_dim_key_defaults={"token_logits": 1},
+            batch_dim_key_defaults={"token_logits": 0, "log_probs_seqs": 0, "predictions": 0},
+            seq_dim_key_defaults={"token_logits": 1, "log_probs_seqs": 1},
             files_per_subdir=files_per_subdir,
             save_all_model_parallel_ranks=False,  # only write one copy of predictions.
         )
     ]

496-511: Explicitly handle LoRA path for Mamba models.

Warn or raise if a LoRA checkpoint is provided since it’s not applied for Mamba.

     else:  # mamba
+        if lora_checkpoint_path:
+            logger.warning(
+                f"LoRA checkpoint provided ({lora_checkpoint_path}) but LoRA is not applied for Mamba models; ignoring."
+            )
         if model_size not in MAMBA_MODEL_OPTIONS:
             raise ValueError(f"Invalid model size for Mamba: {model_size}")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5db2c32 and c7d8b34.

📒 Files selected for processing (4)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (7 hunks)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (2 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (1 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py
🧰 Additional context used
🧬 Code graph analysis (2)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (3)
sub-packages/bionemo-testing/src/bionemo/testing/subprocess_utils.py (1)
  • run_command_in_subprocess (108-129)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
  • predict_cmd (63-71)
  • small_training_finetune_cmd (40-60)
sub-packages/bionemo-testing/src/bionemo/testing/data/fasta.py (1)
  • create_fasta_file (28-63)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (2)
sub-packages/bionemo-evo2/src/bionemo/evo2/models/peft.py (1)
  • Evo2LoRA (26-279)
sub-packages/bionemo-llm/src/bionemo/llm/lightning.py (1)
  • configure_model (307-329)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (5)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)

1-1: Move test helper out of the library namespace to avoid shadowing bionemo.

Place this under a tests-only path (e.g., tests/helpers/evo2/common.py) and update imports. Keeping tests/bionemo/... risks overshadowing the real package during test runs.


63-71: Fix docstring typo (“fro” → “for”).

Also safe to keep signature spacing consistent.

-def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str = ""):
-    """Command fro predict."""
+def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str = ""):
+    """Command for predict."""
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (2)

34-34: LGTM: missing import fixed.


37-37: LGTM: helper import added.

sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (1)

272-276: Guard trainer access in configure_model to avoid AttributeError.

self.trainer may be None when this runs.

     def configure_model(self, *args, **kwargs) -> None:
         """Configure the model."""
         super().configure_model(*args, **kwargs)
-        self.trainer.strategy._init_model_parallel = True
+        trainer = getattr(self, "trainer", None)
+        if trainer is not None and getattr(trainer, "strategy", None) is not None:
+            trainer.strategy._init_model_parallel = True

@balvisio
Copy link
Collaborator Author

/ok to test 55a085e

@balvisio balvisio force-pushed the dev/ba/BIONEMO-2617-support-predict-evo2lora branch from 55a085e to f1df1d7 Compare September 26, 2025 01:55
@balvisio
Copy link
Collaborator Author

/ok to test f1df1d7

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (2)

386-386: Reduce flakiness: bump finetune steps slightly.

Two steps may sometimes be too little to produce observable differences. Consider 4.

Apply this diff:

-num_steps = 2
+num_steps = 4

420-433: Make checkpoint discovery robust across naming schemes.

Matching by suffix "{steps}.0-last" is brittle. Prefer the most recent subdir by mtime.

Apply this diff:

-    expected_checkpoint_suffix = f"{num_steps}.0-last"
-    # Check if any subfolder ends with the expected suffix
-    matching_subfolders = [
-        p for p in checkpoints_dir.iterdir() if p.is_dir() and (expected_checkpoint_suffix in p.name)
-    ]
-
-    assert matching_subfolders, (
-        f"No checkpoint subfolder ending with '{expected_checkpoint_suffix}' found in {checkpoints_dir}."
-    )
-
-    result_dir_peft = tmp_path / "results_peft"
-    additional_args = f"--lora-checkpoint-path {matching_subfolders[0]}"
+    # Pick the most recent checkpoint subfolder (portable across backends)
+    subdirs = [p for p in checkpoints_dir.iterdir() if p.is_dir()]
+    assert subdirs, f"No checkpoint subfolders found in {checkpoints_dir}."
+    lora_ckpt_dir = max(subdirs, key=lambda p: p.stat().st_mtime)
+
+    result_dir_peft = tmp_path / "results_peft"
+    additional_args = f"--lora-checkpoint-path {lora_ckpt_dir}"
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (1)

441-450: Include mappings for log_probs_seqs in PredictionWriter.

Ensures per-token mode and future outputs write correctly without relying on implicit defaults.

Apply this diff:

 callbacks = [
     PredictionWriter(
         output_dir=output_dir,
         write_interval=write_interval,
-        batch_dim_key_defaults={"token_logits": 0},
-        seq_dim_key_defaults={"token_logits": 1},
+        batch_dim_key_defaults={"token_logits": 0, "log_probs_seqs": 0},
+        seq_dim_key_defaults={"token_logits": 1, "log_probs_seqs": 1},
         files_per_subdir=files_per_subdir,
         save_all_model_parallel_ranks=False,  # only write one copy of predictions.
     )
 ]
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c7d8b34 and f1df1d7.

📒 Files selected for processing (4)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (7 hunks)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (2 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (1 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py
🧰 Additional context used
🧬 Code graph analysis (2)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (3)
sub-packages/bionemo-testing/src/bionemo/testing/subprocess_utils.py (1)
  • run_command_in_subprocess (108-129)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
  • predict_cmd (63-71)
  • small_training_finetune_cmd (40-60)
sub-packages/bionemo-testing/src/bionemo/testing/data/fasta.py (1)
  • create_fasta_file (28-63)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (3)
sub-packages/bionemo-evo2/src/bionemo/evo2/models/peft.py (1)
  • Evo2LoRA (26-279)
sub-packages/bionemo-llm/src/bionemo/llm/lightning.py (1)
  • configure_model (307-329)
sub-packages/bionemo-llm/src/bionemo/llm/utils/callbacks.py (1)
  • PredictionWriter (38-225)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: pre-commit
  • GitHub Check: Analyze (rust)
🔇 Additional comments (6)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)

63-71: Fix docstring typo in predict_cmd.

Change "fro" to "for".

Apply this diff:

 def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str = ""):
-    """Command fro predict."""
+    """Command for predict."""

63-71: Avoid tests creating a top-level bionemo package (module shadowing).

This helper under tests/bionemo/... can shadow the real bionemo package during test runs. Move it to a tests-only namespace (e.g., tests/helpers/evo2/common.py) and update imports.

sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (2)

34-38: LGTM: required imports added.

Adds missing utilities; resolves prior F821 failures.


434-436: Consider relaxing the log assertions to avoid brittleness.

Exact substrings can change with upstream logging. If this flakes, drop or soften these checks; the logits inequality already validates LoRA was applied.

sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (2)

272-276: Guard trainer access in HyenaPredictor.configure_model.

Accessing self.trainer.strategy unguarded can raise if called before Trainer attach.

Apply this diff:

 def configure_model(self, *args, **kwargs) -> None:
     """Configure the model."""
     super().configure_model(*args, **kwargs)
-    self.trainer.strategy._init_model_parallel = True
+    trainer = getattr(self, "trainer", None)
+    if trainer is not None and getattr(trainer, "strategy", None) is not None:
+        trainer.strategy._init_model_parallel = True

495-511: Warn when LoRA path is provided for Mamba models (unsupported).

Currently ignored silently; emit a clear warning for user visibility.

Apply this diff:

 else:  # mamba
-    if model_size not in MAMBA_MODEL_OPTIONS:
+    if model_size not in MAMBA_MODEL_OPTIONS:
         raise ValueError(f"Invalid model size for Mamba: {model_size}")
+    if lora_checkpoint_path:
+        logger.warning(
+            f"LoRA checkpoint provided ({lora_checkpoint_path}) but LoRA is not applied for Mamba models; ignoring."
+        )
     config = MAMBA_MODEL_OPTIONS[model_size](
         forward_step_fn=hyena_predict_forward_step,  # Can reuse the same forward steps
         data_step_fn=hyena_predict_data_step,
         distribute_saved_activations=False if sequence_parallel and tensor_parallel_size > 1 else True,
         **config_modifiers_init,
     )

@codecov-commenter
Copy link

codecov-commenter commented Sep 26, 2025

Codecov Report

❌ Patch coverage is 61.29032% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.75%. Comparing base (fc1cb60) to head (014de80).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...kages/bionemo-evo2/src/bionemo/evo2/run/predict.py 70.37% 8 Missing ⚠️
...ackages/bionemo-evo2/src/bionemo/evo2/run/train.py 0.00% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1065      +/-   ##
==========================================
- Coverage   79.76%   79.75%   -0.02%     
==========================================
  Files         156      156              
  Lines       11616    11628      +12     
==========================================
+ Hits         9266     9274       +8     
- Misses       2350     2354       +4     
Files with missing lines Coverage Δ
...ackages/bionemo-evo2/src/bionemo/evo2/run/train.py 12.41% <0.00%> (-0.13%) ⬇️
...kages/bionemo-evo2/src/bionemo/evo2/run/predict.py 83.98% <70.37%> (-0.29%) ⬇️

... and 1 file with indirect coverage changes

@balvisio balvisio added this pull request to the merge queue Sep 26, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Sep 26, 2025
@balvisio balvisio force-pushed the dev/ba/BIONEMO-2617-support-predict-evo2lora branch from f1df1d7 to 014de80 Compare October 4, 2025 17:17
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (1)

852-853: NameError persists when --lora-finetune is used with Mamba/Llama.

lora_transform is only defined in the Hyena branch (line 814), but line 853 attempts to append it for any model type when --lora-finetune is set. This will raise NameError if a user combines --lora-finetune with a Mamba or Llama model.

Apply this diff to guard the callback append:

-    if args.lora_finetune:
-        callbacks.append(lora_transform)
+    if args.lora_finetune:
+        if model_type == "hyena":
+            callbacks.append(lora_transform)
+        else:
+            raise ValueError(f"LoRA fine-tuning is only supported for Hyena models, not {model_type}")

Alternatively, initialize lora_transform = None before the model type branching (line 804) and only append when not None:

+    lora_transform = None
+
     # Create model based on selected model type
     if model_type == "hyena":
         ...
-        lora_transform = None
         ...
     
-    if args.lora_finetune:
+    if lora_transform is not None:
         callbacks.append(lora_transform)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (1)

495-510: Mamba silently ignores LoRA checkpoint path.

If lora_checkpoint_path is provided but model_type="mamba", the LoRA checkpoint is silently ignored. This can lead to user confusion when predictions don't reflect the expected fine-tuned behavior.

Add an explicit check before creating the Mamba model:

     else:  # mamba
+        if lora_checkpoint_path:
+            raise ValueError(
+                f"LoRA checkpoints are not supported for Mamba models. "
+                f"Provided checkpoint path: {lora_checkpoint_path}"
+            )
+
         if model_size not in MAMBA_MODEL_OPTIONS:

Or emit a warning if raising is too strict:

     else:  # mamba
+        if lora_checkpoint_path:
+            logger.warning(
+                f"LoRA checkpoint path provided ({lora_checkpoint_path}) but LoRA is not supported "
+                f"for Mamba models. The checkpoint will be ignored."
+            )
+
         if model_size not in MAMBA_MODEL_OPTIONS:
🧹 Nitpick comments (4)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (1)

639-650: Clarify LoRA parameter descriptions in help text.

The help text for --lora-alpha and --lora-dim is generic. Consider improving clarity:

  • --lora-alpha: "LoRA scaling parameter (alpha)" instead of "Alpha parameter for LoRA fine-tuning."
  • --lora-dim: "LoRA rank (dimension)" instead of "Dim parameter for LoRA fine-tuning."

Apply this diff to improve the help text:

     parser.add_argument(
         "--lora-alpha",
         type=int,
         default=None,
-        help="Alpha parameter for LoRA fine-tuning.",
+        help="LoRA scaling parameter (alpha).",
     )
     parser.add_argument(
         "--lora-dim",
         type=int,
         default=None,
-        help="Dim parameter for LoRA fine-tuning.",
+        help="LoRA rank (dimension).",
     )
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (3)

163-169: Clarify help text for --lora-checkpoint-path.

Consider improving the help text for clarity:

Apply this diff:

     ap.add_argument(
         "--lora-checkpoint-path",
         type=Path,
         required=False,
         default=None,
-        help="Path to the lora states to restore from.",
+        help="Path to the LoRA adapter checkpoint directory.",
     )

441-450: Document multi-device limitation with epoch write interval.

The write_interval parameter is now configurable, but PredictionWriter will raise an error if write_interval="epoch" is used with num_devices > 1. Consider adding validation or documentation.

Add validation in the predict function before the callbacks list:

+    if write_interval == "epoch" and devices > 1:
+        raise ValueError(
+            "write_interval='epoch' is not supported with multiple devices. "
+            "Please use write_interval='batch' instead."
+        )
+
     callbacks = [
         PredictionWriter(

Alternatively, document this limitation in the CLI help text:

     ap.add_argument(
         "--write-interval",
         type=str,
         default="epoch",
         choices=["epoch", "batch"],
-        help="Interval to write predictions to disk. If doing very large predictions, you may want to set this to 'batch'.",
+        help="Interval to write predictions to disk. Use 'batch' when using multiple devices. If doing very large predictions, you may want to set this to 'batch'.",
     )

445-446: Add dimension defaults for all output keys.

The PredictionWriter only specifies dimension defaults for token_logits, but the model can also output log_probs_seqs (when output_log_prob_seqs=True) and potentially other keys. Missing dimension defaults may cause issues during batch collation.

Based on the model's output in predict_step (lines 250-266), add dimension defaults for all keys:

         PredictionWriter(
             output_dir=output_dir,
             write_interval=write_interval,
-            batch_dim_key_defaults={"token_logits": 0},
-            seq_dim_key_defaults={"token_logits": 1},
+            batch_dim_key_defaults={"token_logits": 0, "log_probs_seqs": 0, "pad_mask": 0, "seq_idx": 0},
+            seq_dim_key_defaults={"token_logits": 1, "log_probs_seqs": 1, "pad_mask": 1},
             files_per_subdir=files_per_subdir,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1df1d7 and 014de80.

📒 Files selected for processing (4)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (7 hunks)
  • sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (2 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (1 hunks)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py
🧰 Additional context used
🧬 Code graph analysis (2)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (1)
sub-packages/bionemo-evo2/src/bionemo/evo2/models/peft.py (1)
  • Evo2LoRA (26-279)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (3)
sub-packages/bionemo-testing/src/bionemo/testing/subprocess_utils.py (1)
  • run_command_in_subprocess (108-129)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
  • predict_cmd (63-71)
  • small_training_finetune_cmd (40-60)
sub-packages/bionemo-testing/src/bionemo/testing/data/fasta.py (1)
  • create_fasta_file (28-63)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: changed-files
  • GitHub Check: pre-commit
  • GitHub Check: changed-dirs
  • GitHub Check: Analyze (rust)
🔇 Additional comments (6)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (1)

816-825: LGTM: LoRA kwargs correctly assembled and passed.

The dict comprehension properly filters None values before passing hyperparameters to Evo2LoRA. This ensures only explicitly set CLI options are forwarded.

sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (2)

34-37: LGTM: Imports correctly added for new test utilities.

The imports for run_command_in_subprocess and the test command helpers (predict_cmd, small_training_finetune_cmd) are properly added to support the new LoRA prediction test.


372-452: LGTM: Comprehensive PEFT comparison test.

The test validates end-to-end LoRA integration by:

  1. Fine-tuning a base model with LoRA
  2. Running predictions with and without the LoRA checkpoint
  3. Verifying that logits differ while maintaining consistent shapes and sequence indices

The test correctly asserts:

  • Base model restoration without adapters during fine-tuning (lines 400-401)
  • Adapter loading during PEFT prediction (lines 434-435)
  • Result divergence between base and PEFT predictions (line 449)
  • Shape consistency (lines 450-452)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (3)

44-44: LGTM: Evo2LoRA import correctly updated.

The import path for Evo2LoRA is now correct (bionemo.evo2.models.peft), aligning with the module's actual location.


539-539: LGTM: Centralized callbacks list.

The callbacks are now properly managed in a centralized list and passed to the Trainer, improving maintainability.


599-599: LGTM: LoRA checkpoint path correctly forwarded.

The lora_checkpoint_path argument is properly passed from CLI args to the predict() function.

@balvisio balvisio added this pull request to the merge queue Oct 4, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Oct 4, 2025
@balvisio balvisio force-pushed the dev/ba/BIONEMO-2617-support-predict-evo2lora branch from 014de80 to 81680c9 Compare October 5, 2025 01:02
@balvisio balvisio enabled auto-merge October 5, 2025 01:43
Signed-off-by: Bruno Alvisio <balvisio@nvidia.com>
@balvisio balvisio force-pushed the dev/ba/BIONEMO-2617-support-predict-evo2lora branch from 81680c9 to 5896940 Compare October 5, 2025 02:06
@balvisio balvisio added the ciflow:skip Skip all CI tests for this PR label Oct 5, 2025
@balvisio
Copy link
Collaborator Author

balvisio commented Oct 5, 2025

/ok to test 5896940

@balvisio balvisio added this pull request to the merge queue Oct 5, 2025
Merged via the queue into main with commit b85d5f9 Oct 5, 2025
19 checks passed
@balvisio balvisio deleted the dev/ba/BIONEMO-2617-support-predict-evo2lora branch October 5, 2025 03:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ciflow:skip Skip all CI tests for this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants